<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  
  <title>idea插件开发（实现@SingleInstance注解增加getInstance方法） | SictiyLeon</title>
  
  

  

  <meta name="HandheldFriendly" content="True" />
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <!-- meta -->
  
  
  <meta name='theme-color' content='#FFFFFF'>
  <meta name='msapplication-TileColor' content='#1BC3FB'>
  <meta name='msapplication-config' content='https://cdn.jsdelivr.net/gh/xaoxuu/cdn-favicon@19.9.6/browserconfig.xml'>
  

  <!-- link -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.css" />
  
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/node-waves@0.7.6/dist/waves.min.css">
  
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.10.1/css/all.min.css">
  
  
  <link rel='shortcut icon' type='image/x-icon' href='https://cdn.jsdelivr.net/gh/xaoxuu/cdn-favicon@19.9.6/favicon.ico'>
  <link rel='icon' type='image/x-icon' sizes='32x32' href='https://cdn.jsdelivr.net/gh/xaoxuu/cdn-favicon@19.9.6/favicon-32x32.png'>
  <link rel='apple-touch-icon' type='image/png' sizes='180x180' href='https://cdn.jsdelivr.net/gh/xaoxuu/cdn-favicon@19.9.6/apple-touch-icon.png'>
  <link rel='mask-icon' color='#1BC3FB' href='https://cdn.jsdelivr.net/gh/xaoxuu/cdn-favicon@19.9.6/safari-pinned-tab.svg'>
  <link rel='manifest' href='https://cdn.jsdelivr.net/gh/xaoxuu/cdn-favicon@19.9.6/site.webmanifest'>
  

  

  
    
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/xaoxuu/cdn-material-x@19.11.26/css/style.css">

  

  <script>
    function setLoadingBarProgress(num) {
      document.getElementById('loading-bar').style.width=num+"%";
    }
  </script>

  
  
<meta name="generator" content="Hexo 4.2.0"></head>

<body>
  
  
  <div class="cover-wrapper">
    <cover class='cover post half'>
      
        
  <h1 class='title'>SictiyLeon</h1>


  <div class="m_search">
    <form name="searchform" class="form u-search-form">
      <input type="text" class="input u-search-input" placeholder="" />
      <i class="icon fas fa-search fa-fw"></i>
    </form>
  </div>

<div class='menu navgation'>
  <ul class='h-list'>
    
      
        <li>
          <a class="nav home" href="/hblog/"
            
            
            id="hblog">
            <i class='fas fa-rss fa-fw'></i>&nbsp;首页
          </a>
        </li>
      
        <li>
          <a class="nav home" href="/hblog/categories/"
            
              rel="nofollow"
            
            
            id="hblogcategories">
            <i class='fas fa-code-branch fa-fw'></i>&nbsp;分类
          </a>
        </li>
      
        <li>
          <a class="nav home" href="/hblog/archives/"
            
              rel="nofollow"
            
            
            id="hblogarchives">
            <i class='fas fa-link fa-fw'></i>&nbsp;归档
          </a>
        </li>
      
        <li>
          <a class="nav home" href="/hblog/about/"
            
              rel="nofollow"
            
            
            id="hblogabout">
            <i class='fas fa-info-circle fa-fw'></i>&nbsp;关于
          </a>
        </li>
      
    
  </ul>
</div>

      
    </cover>
    <header class="l_header material">
  <div id="loading-bar-wrapper">
    <div id="loading-bar" class="material"></div>
  </div>

	<div class='wrapper'>
		<div class="nav-main container container--flex">
      <a class="logo flat-box" href='/hblog/' >
        
          SictiyLeon
        
      </a>
			<div class='menu navgation'>
				<ul class='h-list'>
          
  					
  						<li>
								<a class="nav flat-box" href="/hblog/"
                  
                  
                  id="hblog">
									<i class='fas fa-grin fa-fw'></i>&nbsp;首页
								</a>
							</li>
      			
  						<li>
								<a class="nav flat-box" href="/hblog/categories/"
                  
                    rel="nofollow"
                  
                  
                  id="hblogcategories">
									<i class='fas fa-folder-open fa-fw'></i>&nbsp;分类
								</a>
							</li>
      			
  						<li>
								<a class="nav flat-box" href="/hblog/archives/"
                  
                    rel="nofollow"
                  
                  
                  id="hblogarchives">
									<i class='fas fa-archive fa-fw'></i>&nbsp;归档
								</a>
							</li>
      			
      		
				</ul>
			</div>

			
				<div class="m_search">
					<form name="searchform" class="form u-search-form">
						<input type="text" class="input u-search-input" placeholder="Search" />
						<i class="icon fas fa-search fa-fw"></i>
					</form>
				</div>
			
			<ul class='switcher h-list'>
				
					<li class='s-search'><a class="fas fa-search fa-fw" href='javascript:void(0)'></a></li>
				
				<li class='s-menu'><a class="fas fa-bars fa-fw" href='javascript:void(0)'></a></li>
			</ul>
		</div>

		<div class='nav-sub container container--flex'>
			<a class="logo flat-box"></a>
			<ul class='switcher h-list'>
				<li class='s-comment'><a class="flat-btn fas fa-comments fa-fw" href='javascript:void(0)'></a></li>
        
          <li class='s-toc'><a class="flat-btn fas fa-list fa-fw" href='javascript:void(0)'></a></li>
        
			</ul>
		</div>
	</div>
</header>
	<aside class="menu-phone">
    <header>
		<nav class="menu navgation">
      <ul>
        
          
            <li>
							<a class="nav flat-box" href="/hblog/"
                
                
                id="hblog">
								<i class='fas fa-clock fa-fw'></i>&nbsp;首页
							</a>
            </li>
          
            <li>
							<a class="nav flat-box" href="/hblog/categories/"
                
                  rel="nofollow"
                
                
                id="hblogcategories">
								<i class='fas fa-folder-open fa-fw'></i>&nbsp;分类
							</a>
            </li>
          
            <li>
							<a class="nav flat-box" href="/hblog/archives/"
                
                  rel="nofollow"
                
                
                id="hblogarchives">
								<i class='fas fa-archive fa-fw'></i>&nbsp;归档
							</a>
            </li>
          
            <li>
							<a class="nav flat-box" href="/hblog/about/"
                
                  rel="nofollow"
                
                
                id="hblogabout">
								<i class='fas fa-info-circle fa-fw'></i>&nbsp;关于
							</a>
            </li>
          
       
      </ul>
		</nav>
    </header>
	</aside>
<script>setLoadingBarProgress(40);</script>

  </div>


  <div class="l_body">
    <div class='body-wrapper'>
      <div class='l_main'>
  

  <article id="post" class="post white-box article-type-post" itemscope itemprop="blogPost">
    


  <section class='meta'>
    
    
    <div class="meta" id="header-meta">
      
        
  
    <h1 class="title">
      <a href="/hblog/2020/03/16/idea%E6%8F%92%E4%BB%B6/">
        idea插件开发（实现@SingleInstance注解增加getInstance方法）
      </a>
    </h1>
  


      
      <div class='new-meta-box'>
        
          
        
          
            
  <div class='new-meta-item author'>
    
      <a href="https://www.sictiy.cn/hblog" rel="nofollow">
        
          <i class="fas fa-user" aria-hidden="true"></i>
        
        <p>sictiy xu</p>
      </a>
    
  </div>


          
        
          
            <div class="new-meta-item date">
  <a class='notlink'>
    <i class="fas fa-calendar-alt" aria-hidden="true"></i>
    <p>2020-03-16</p>
  </a>
</div>

          
        
          
            
  
  <div class='new-meta-item category'>
    <a href='/hblog/categories/java/' rel="nofollow">
      <i class="fas fa-folder-open" aria-hidden="true"></i>
      <p>java</p>
    </a>
  </div>


          
        
          
            
  
    <div class="new-meta-item browse busuanzi">
      <a class='notlink'>
        <i class="fas fa-eye" aria-hidden="true"></i>
        <p>
          <span id="busuanzi_value_page_pv">
            <i class="fas fa-spinner fa-spin fa-fw" aria-hidden="true"></i>
          </span>
        </p>
      </a>
    </div>
  


          
        
          
            
  
    <div class="new-meta-item wordcount">
      <a class='notlink'>
        <i class="fas fa-keyboard" aria-hidden="true"></i>
        <p>字数统计:</p>
        <p>2.6k字</p>
      </a>
    </div>
    <div class="new-meta-item readtime">
      <a class='notlink'>
        <i class="fas fa-hourglass-half" aria-hidden="true"></i>
        <p>阅读时长≈</p>
        <p>12分</p>
      </a>
    </div>
  

          
        
          
            

          
        
      </div>
      
        <hr>
      
    </div>
  </section>


    <section class="article typo">
      <div class="article-entry" itemprop="articleBody">
        <p>之前实现了@SingleInstance注解的编译器处理器，实现添加<code>@SingleInstance</code>就可以把类变成单例，这里记录一下实现让idea也能看懂这个注解的的插件的过程。</p>
<a id="more"></a>

<h2 id="Lombok-Idea-插件修改"><a href="#Lombok-Idea-插件修改" class="headerlink" title="Lombok Idea 插件修改"></a>Lombok Idea 插件修改</h2><h3 id="跑一下原Lombok-Idea-插件"><a href="#跑一下原Lombok-Idea-插件" class="headerlink" title="跑一下原Lombok Idea 插件"></a>跑一下原Lombok Idea 插件</h3><p>先去<a href="https://github.com/mplushnikov/lombok-intellij-plugin" target="_blank" rel="noopener">github</a> clone一份代码到本地</p>
<p>用idea打开项目，这是一个gradle项目，项目配置文件为build.gradle，打开这个文件找到repositories修改gradle仓库地址：</p>
<figure class="highlight gradle"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">repositories</span>&#123;</span><br><span class="line">  mavenLocal()</span><br><span class="line">  maven &#123; url <span class="string">'http://maven.aliyun.com/nexus/content/repositories/google'</span> &#125;</span><br><span class="line">  maven &#123; url <span class="string">'http://maven.aliyun.com/nexus/content/groups/public/'</span> &#125;</span><br><span class="line">  maven &#123; url <span class="string">'http://maven.aliyun.com/nexus/content/repositories/jcenter'</span> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在右边的gradle面板中点击reimport 一段时间后就下载好了相应的依赖，最大的应该是ideaIC***，也就是idea社区版的依赖 应该有好几百m</p>
<p>然后点开task-&gt; intellij 就可以看到很多gradle任务，运行一下runlde发现可以跑起来了。</p>
<h3 id="阅读Lombok-Idea-插件源码"><a href="#阅读Lombok-Idea-插件源码" class="headerlink" title="阅读Lombok Idea 插件源码"></a>阅读Lombok Idea 插件源码</h3><p>大致看一下java目录下各目录都是干嘛的：</p>
<ul>
<li>action：顶部菜单新插入的action按钮</li>
<li>extension：功能扩展</li>
<li>processor：注解处理器</li>
<li>provider：同样提供功能扩展</li>
<li>ps: 扩展的psi元素</li>
<li>util: 一些共用的工具类</li>
</ul>
<p>再看找一个跟<code>@SingleInstance</code>处理类似的注解，看是怎么处理的。这里选择<code>@ToString</code>,因为不关注添加的方法具体细节，两个注解的逻辑是一样的，都是如果有这个注解，就判断类有没有特定名称的方法，如果没有则添加一个。通过该类可以找到提供注解处理的类为:<code>ToStringProcessor</code>，然后搜索该类结果为：<img src="https://s1.ax1x.com/2020/03/16/8J4XLt.png" alt="searchToString"></p>
<p>推测依据<code>ToStringProcessor</code> 再实现一个<code>SingleInstanceProcessor</code>, 然后再搜索到的这些类的对应位置也加上新的处理类就能完成需求了，其中最重要到的显然就是实现这个Processor。</p>
<p>不管整体的逻辑原理是啥，先来看一下<code>ToStringProcessor</code>内部，到底是个什么逻辑，要实现一个新的处理器需要干些啥</p>
<p>先是构造函数:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">ToStringProcessor</span><span class="params">(@NotNull EqualsAndHashCodeToStringHandler equalsAndHashCodeToStringHandler)</span> </span>&#123;</span><br><span class="line">  <span class="keyword">super</span>(PsiMethod.class, ToString.class);</span><br><span class="line">  handler = equalsAndHashCodeToStringHandler;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这个点进去super里面查看父类的构造函数就可以知道，这个是定义这个处理器支持的psi元素类型以及需要处理的的注解类型。</p>
<p>然后是validate和几个validate开头的私有函数：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">boolean</span> <span class="title">validate</span><span class="params">(@NotNull PsiAnnotation psiAnnotation, @NotNull PsiClass psiClass, @NotNull ProblemBuilder builder)</span> </span>&#123;</span><br><span class="line">  <span class="keyword">final</span> <span class="keyword">boolean</span> result = validateAnnotationOnRigthType(psiClass, builder);</span><br><span class="line">  <span class="keyword">if</span> (result) &#123;</span><br><span class="line">    validateExistingMethods(psiClass, builder);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">final</span> Collection&lt;String&gt; excludeProperty = PsiAnnotationUtil.getAnnotationValues(psiAnnotation, <span class="string">"exclude"</span>, String.class);</span><br><span class="line">  <span class="keyword">final</span> Collection&lt;String&gt; ofProperty = PsiAnnotationUtil.getAnnotationValues(psiAnnotation, <span class="string">"of"</span>, String.class);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (!excludeProperty.isEmpty() &amp;&amp; !ofProperty.isEmpty()) &#123;</span><br><span class="line">    builder.addWarning(<span class="string">"exclude and of are mutually exclusive; the 'exclude' parameter will be ignored"</span>,</span><br><span class="line">      PsiQuickFixFactory.createChangeAnnotationParameterFix(psiAnnotation, <span class="string">"exclude"</span>, <span class="keyword">null</span>));</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    validateExcludeParam(psiClass, builder, psiAnnotation, excludeProperty);</span><br><span class="line">  &#125;</span><br><span class="line">  validateOfParam(psiClass, builder, psiAnnotation, ofProperty);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">validateAnnotationOnRigthType</span><span class="params">(@NotNull PsiClass psiClass, @NotNull ProblemBuilder builder)</span> </span>&#123;</span><br><span class="line">  <span class="keyword">boolean</span> result = <span class="keyword">true</span>;</span><br><span class="line">  <span class="keyword">if</span> (psiClass.isAnnotationType() || psiClass.isInterface()) &#123;</span><br><span class="line">    builder.addError(<span class="string">"@ToString is only supported on a class or enum type"</span>);</span><br><span class="line">    result = <span class="keyword">false</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>看一下是哪里调用了这个validate:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">PsiAnnotation psiAnnotation = PsiAnnotationSearchUtil.findAnnotation(psiClass, getSupportedAnnotationClasses());</span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">null</span> != psiAnnotation) &#123;</span><br><span class="line">  <span class="keyword">if</span> (supportAnnotationVariant(psiAnnotation) &amp;&amp; validate(psiAnnotation, psiClass, ProblemEmptyBuilder.getInstance())) &#123;</span><br><span class="line">    result = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">    generatePsiElements(psiClass, psiAnnotation, result);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这样就清晰了，validate是用来判断需不需要处理的，generatePsiElements是实现具体的处理逻辑，就是修改psi元素。</p>
<p>回到ToStringProcessor, 果然马上就是generatePsiElements方法的实现：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">generatePsiElements</span><span class="params">(@NotNull PsiClass psiClass, @NotNull PsiAnnotation psiAnnotation, @NotNull List&lt;? <span class="keyword">super</span> PsiElement&gt; target)</span> </span>&#123;</span><br><span class="line">  target.addAll(createToStringMethod(psiClass, psiAnnotation));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@NotNull</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> PsiMethod <span class="title">createToStringMethod</span><span class="params">(@NotNull PsiClass psiClass, @NotNull Collection&lt;MemberInfo&gt; memberInfos, @NotNull PsiAnnotation psiAnnotation, <span class="keyword">boolean</span> forceCallSuper)</span> </span>&#123;</span><br><span class="line">  <span class="keyword">final</span> PsiManager psiManager = psiClass.getManager();</span><br><span class="line"></span><br><span class="line">  <span class="keyword">final</span> String paramString = createParamString(psiClass, memberInfos, psiAnnotation, forceCallSuper);</span><br><span class="line">  <span class="keyword">final</span> String blockText = String.format(<span class="string">"return \"%s(%s)\";"</span>, getSimpleClassName(psiClass), paramString);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">final</span> LombokLightMethodBuilder methodBuilder = <span class="keyword">new</span> LombokLightMethodBuilder(psiManager, METHOD_NAME)</span><br><span class="line">    .withMethodReturnType(PsiType.getJavaLangString(psiManager, GlobalSearchScope.allScope(psiClass.getProject())))</span><br><span class="line">    .withContainingClass(psiClass)</span><br><span class="line">    .withNavigationElement(psiAnnotation)</span><br><span class="line">    .withModifier(PsiModifier.PUBLIC);</span><br><span class="line">  methodBuilder.withBody(PsiMethodUtil.createCodeBlockFromText(blockText, methodBuilder));</span><br><span class="line">  <span class="keyword">return</span> methodBuilder;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这里就是需要创建一个ToString方法。</p>
<h3 id="新增-SingleInstance的处理"><a href="#新增-SingleInstance的处理" class="headerlink" title="新增@SingleInstance的处理"></a>新增<code>@SingleInstance</code>的处理</h3><p>先参照<code>ToStringProcessor</code> 实现一个<code>SingleInstance</code>, 有一些明显不需要的就直接删掉：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> sictiy.xu</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@version</span> 2019/10/14 15:31</span></span><br><span class="line"><span class="comment"> **/</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SingleInstanceProcessor</span> <span class="keyword">extends</span> <span class="title">AbstractClassProcessor</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String METHOD_NAME = <span class="string">"getInstance"</span>;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">protected</span> <span class="title">SingleInstanceProcessor</span><span class="params">()</span></span></span><br><span class="line"><span class="function">  </span>&#123;</span><br><span class="line">    <span class="keyword">super</span>(PsiMethod.class, SingleInstance.class);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Override</span></span><br><span class="line">  <span class="function"><span class="keyword">protected</span> <span class="keyword">boolean</span> <span class="title">validate</span><span class="params">(@NotNull PsiAnnotation psiAnnotation, @NotNull PsiClass psiClass, @NotNull ProblemBuilder builder)</span></span></span><br><span class="line"><span class="function">  </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (!validateAnnotationOnRigthType(psiClass, builder))</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (!validateExistingMethods(psiClass, builder))</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">validateAnnotationOnRigthType</span><span class="params">(@NotNull PsiClass psiClass, @NotNull ProblemBuilder builder)</span></span></span><br><span class="line"><span class="function">  </span>&#123;</span><br><span class="line">    <span class="keyword">boolean</span> result = <span class="keyword">true</span>;</span><br><span class="line">    <span class="keyword">if</span> (psiClass.isAnnotationType() || psiClass.isInterface())</span><br><span class="line">    &#123;</span><br><span class="line">      builder.addError(<span class="string">"@SingleInstance is only supported on a class or enum type"</span>);</span><br><span class="line">      result = <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">validateExistingMethods</span><span class="params">(@NotNull PsiClass psiClass, @NotNull ProblemBuilder builder)</span></span></span><br><span class="line"><span class="function">  </span>&#123;</span><br><span class="line">    <span class="keyword">boolean</span> result = <span class="keyword">true</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">final</span> Collection&lt;PsiMethod&gt; classMethods = PsiClassUtil.collectClassMethodsIntern(psiClass);</span><br><span class="line">    <span class="keyword">if</span> (PsiMethodUtil.hasMethodByName(classMethods, METHOD_NAME))</span><br><span class="line">    &#123;</span><br><span class="line">      builder.addWarning(<span class="string">"Not generated '%s'(): A method with same name already exists"</span>, METHOD_NAME);</span><br><span class="line">      result = <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Override</span></span><br><span class="line">  <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">generatePsiElements</span><span class="params">(@NotNull PsiClass psiClass, @NotNull PsiAnnotation psiAnnotation, @NotNull List&lt;? <span class="keyword">super</span> PsiElement&gt; target)</span></span></span><br><span class="line"><span class="function">  </span>&#123;</span><br><span class="line">    target.addAll(createGetInstanceMethod(psiClass, psiAnnotation));</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">private</span> Collection&lt;PsiMethod&gt; <span class="title">createGetInstanceMethod</span><span class="params">(@NotNull PsiClass psiClass, @NotNull PsiAnnotation psiAnnotation)</span></span></span><br><span class="line"><span class="function">  </span>&#123;</span><br><span class="line">    <span class="keyword">final</span> PsiMethod method = createOneGetInstanceMethod(psiClass, psiAnnotation);</span><br><span class="line">    <span class="keyword">return</span> Collections.singletonList(method);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">private</span> PsiMethod <span class="title">createOneGetInstanceMethod</span><span class="params">(@NotNull PsiClass psiClass, @NotNull PsiAnnotation psiAnnotation)</span></span></span><br><span class="line"><span class="function">  </span>&#123;</span><br><span class="line">    <span class="keyword">final</span> PsiManager psiManager = psiClass.getManager();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">final</span> LombokLightMethodBuilder methodBuilder = <span class="keyword">new</span> LombokLightMethodBuilder(psiManager, METHOD_NAME);</span><br><span class="line">    methodBuilder.withMethodReturnType(PsiClassUtil.getTypeWithGenerics(psiClass));</span><br><span class="line">    methodBuilder.withContainingClass(psiClass);</span><br><span class="line">    methodBuilder.withNavigationElement(psiAnnotation);</span><br><span class="line">    methodBuilder.withModifier(PsiModifier.PUBLIC);</span><br><span class="line">    methodBuilder.withModifier(PsiModifier.STATIC);</span><br><span class="line">    <span class="keyword">return</span> methodBuilder;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Override</span></span><br><span class="line">  <span class="function"><span class="keyword">public</span> LombokPsiElementUsage <span class="title">checkFieldUsage</span><span class="params">(@NotNull PsiField psiField, @NotNull PsiAnnotation psiAnnotation)</span></span></span><br><span class="line"><span class="function">  </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> LombokPsiElementUsage.NONE;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>然后修改ToStringProcessor涉及到的类，在ToStringProcessor用到的类里，有大部分其实并不需要，最后修改到的地方只有两处，第一个是LombokProcessorManager, 还有一个是plugin.xml里面</p>
<p>修改后编译运行，发现已经可以用了。果然在现有基础上改就是这么简单。</p>
<h2 id="简化体验idea插件开发"><a href="#简化体验idea插件开发" class="headerlink" title="简化体验idea插件开发"></a>简化体验idea插件开发</h2><p>在原有的基础上修改简单，但也仅仅了解了Processor里面的逻辑。但对Processor的process方法什么时候调用，idea怎么触发找到processor等等都不知道的。就算不深入，简单的了解还是要有的, 不然下次需要重头开始就懵了。</p>
<h3 id="plugin-xml"><a href="#plugin-xml" class="headerlink" title="plugin.xml"></a>plugin.xml</h3><p>这个类似于项目文件，插件的名称，作者，更新日志，版本等都在这里配置，同时也相当于实现清单，告诉idea这个插件实现了哪些接口。</p>
<p>配置方面就不说了，直接修改就好了，实现的接口主要分为两部分，actions 和 extensions, 相当于被动技能和主动技能:</p>
<ul>
<li><p>action extends AnAction 扩展菜单栏</p>
<ul>
<li>重写 actionPerformed() action被点击调用</li>
<li>重写 update() 点击菜单栏使action菜单项显示出来时，回调update函数，用于根据当前的idea环境判断action是不是可点击</li>
<li>在plugin.xml 中可以通过add-to-group来指定这个action按钮放在哪</li>
</ul>
</li>
<li><p>extensions 高亮，语法支持等扩展</p>
<ul>
<li>重写 initComponent()</li>
<li>重写 disposeComponent()</li>
<li>application idea 启动的时候初始化</li>
<li>project 打开每个project的时候初始化</li>
<li>module 打开每个module的时候初始化</li>
</ul>
</li>
</ul>
<h3 id="主要功能扩展接口"><a href="#主要功能扩展接口" class="headerlink" title="主要功能扩展接口"></a>主要功能扩展接口</h3><ul>
<li><p>PsiAugmentProvider</p>
<ul>
<li>在idea眼里 所有的类，方法，属性都是psi</li>
<li>该接口可以动态扩展psi元素, 修改psi元素让idea眼中的类，方法等变成我们想要的样子，比如增加一个方法， 修改返回值等。</li>
<li>扩展的方法编译时不会生成 即仅编辑器可见</li>
<li>重写 getAugments() 针对每一个传入的psi元素进行扩展，第二个参数为元素的类型</li>
</ul>
</li>
<li><p>StructureViewExtension</p>
<ul>
<li>扩展Structure功能, 就是经常看到的Structure面板</li>
<li>重写 getChildren() 修改Structure界面看到的子元素 将自己添加的虚拟元素加入返回的结果中即可</li>
</ul>
</li>
</ul>
<h3 id="开始动手"><a href="#开始动手" class="headerlink" title="开始动手"></a>开始动手</h3><h4 id="Action"><a href="#Action" class="headerlink" title="Action"></a>Action</h4><p>实现一个最简单的action：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SingleAction</span> <span class="keyword">extends</span> <span class="title">AnAction</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">actionPerformed</span><span class="params">(@NotNull AnActionEvent anActionEvent)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        LogUtil.info(<span class="string">"click single action"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>然后再plugin.xml中声明一下：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">action</span> <span class="attr">class</span>=<span class="string">"com.sictiy.plugin.action.SingleAction"</span> <span class="attr">id</span>=<span class="string">"singleAction"</span> <span class="attr">text</span>=<span class="string">"SingleAction"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">add-to-group</span> <span class="attr">group-id</span>=<span class="string">"ToolsMenu"</span> <span class="attr">anchor</span>=<span class="string">"first"</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">action</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>然后就可以看到啦：<br><img src="https://s1.ax1x.com/2020/03/16/8J45dK.png" alt="action"></p>
<h4 id="Processor"><a href="#Processor" class="headerlink" title="Processor"></a>Processor</h4><p>先声明我们需要实现的Processor，以及所有Processor的管理组件：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- Add your extensions here --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">applicationService</span> <span class="attr">serviceImplementation</span>=<span class="string">"com.sictiy.plugin.processor.impl.SingleInstanceProcessor"</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">projectService</span> <span class="attr">serviceImplementation</span>=<span class="string">"com.sictiy.plugin.processor.ProcessorComponent"</span>/&gt;</span></span><br></pre></td></tr></table></figure>

<p>然后再实现他们，SingleInstanceProcessor和之前一样，就不说了，ProcessorComponent维护两个hashtable， 以Processor支持的psi元素，和支持的注解为key，缓存所有的Processor实例：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ProcessorComponent</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Map&lt;Class&lt;? extends PsiElement&gt;, Collection&lt;Processor&gt;&gt; classProcessorMap;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Map&lt;String, Collection&lt;Processor&gt;&gt; annotationProcessorMap;</span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Collection&lt;Processor&gt; <span class="title">getClassProcessors</span><span class="params">(@NotNull Class&lt;? extens PsiElement&gt; supportedClass)</span></span></span><br><span class="line"><span class="function">    ...</span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function">    <span class="keyword">public</span> Collection&lt;Processor&gt; <span class="title">getAnnotationProcessors</span><span class="params">(@NotNull PsiAnnotation psiAnnotation)</span></span></span><br><span class="line"><span class="function">    ...</span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function">&#125;</span></span><br></pre></td></tr></table></figure>

<h4 id="Extends"><a href="#Extends" class="headerlink" title="Extends"></a>Extends</h4><h5 id="PsiAugmentProvider"><a href="#PsiAugmentProvider" class="headerlink" title="PsiAugmentProvider"></a>PsiAugmentProvider</h5><p>实现getAugments方法, 根据传入的psi元素和类型，返回处理后的所有元素，这里使用CachedValueProvider来提供缓存功能，已提高获取时的性能。CachedValueProvider 根据psi元素类型的不同，分为三个不同的静态实例，每个实例内部维护一个<code>RecursionGuard&lt;PsiClass&gt;</code>实现缓存, compute方法通过resursionGuard返回处理后的元素列表。在getPsis中，实际处理传入的元素，遍历所有支持该类型的处理器，依次处理，将结果组合成list返回：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyPsiAugmentProvider</span> <span class="keyword">extends</span> <span class="title">PsiAugmentProvider</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">protected</span> &lt;Psi extends PsiElement&gt; <span class="function">List&lt;Psi&gt; <span class="title">getAugments</span><span class="params">(@NotNull PsiElement element, @NotNull Class&lt;Psi&gt; type)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">      ...</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">MyCachedValueProvider</span>&lt;<span class="title">Psi</span> <span class="keyword">extends</span> <span class="title">PsiElement</span>&gt; <span class="keyword">implements</span> <span class="title">CachedValueProvider</span>&lt;<span class="title">List</span>&lt;<span class="title">Psi</span>&gt;&gt;</span></span><br><span class="line"><span class="class">    </span>&#123;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">final</span> Class&lt;Psi&gt; type;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">final</span> PsiClass psiClass;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">final</span> RecursionGuard&lt;PsiClass&gt; recursionGuard;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">private</span> <span class="title">MyCachedValueProvider</span><span class="params">(Class&lt;Psi&gt; type, PsiClass psiClass, String guardName)</span></span></span><br><span class="line"><span class="function">        </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.type = type;</span><br><span class="line">            <span class="keyword">this</span>.psiClass = psiClass;</span><br><span class="line">            <span class="keyword">this</span>.recursionGuard = RecursionManager.createGuard(guardName);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">private</span> <span class="title">MyCachedValueProvider</span><span class="params">(Class&lt;Psi&gt; type, PsiClass psiClass, RecursionGuard&lt;PsiClass&gt; recursionGuard)</span></span></span><br><span class="line"><span class="function">        </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.type = type;</span><br><span class="line">            <span class="keyword">this</span>.psiClass = psiClass;</span><br><span class="line">            <span class="keyword">this</span>.recursionGuard = recursionGuard;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Nullable</span></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> Result&lt;List&lt;Psi&gt;&gt; compute()</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">return</span> recursionGuard.doPreventingRecursion(psiClass, <span class="keyword">true</span>, () -&gt; Result.create(getPsis(psiClass, type), psiClass));</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 根据传入的psi元素， 由相应的处理器 处理后 将结果返回</span></span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">static</span> &lt;Psi extends PsiElement&gt; <span class="function">List&lt;Psi&gt; <span class="title">getPsis</span><span class="params">(PsiClass psiClass, Class&lt;Psi&gt; type)</span></span></span><br><span class="line"><span class="function">        </span>&#123;</span><br><span class="line">            <span class="keyword">final</span> List&lt;Psi&gt; result = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">            <span class="keyword">final</span> Collection&lt;Processor&gt; processors = ProcessorComponent.getInstance(psiClass.getProject()).getClassProcessors(type);</span><br><span class="line">            <span class="keyword">for</span> (Processor processor : processors)</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="keyword">final</span> List&lt;? <span class="keyword">super</span> PsiElement&gt; elements = processor.process(psiClass);</span><br><span class="line">                elements.forEach(element -&gt; result.add((Psi) element));</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> result;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="StructureViewExtension"><a href="#StructureViewExtension" class="headerlink" title="StructureViewExtension"></a>StructureViewExtension</h5><p>重写getChildren() 方法 找出传入元素的所有子元素中属于自己额外添加的那一部分，放到一个列表中返回即可：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyStructureViewExtension</span> <span class="keyword">implements</span> <span class="title">StructureViewExtension</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Class&lt;? extends PsiElement&gt; getType()</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> PsiClass.class;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 将自定义元素添加的structure面板</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> psiElement psiElement</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> com.intellij.ide.structureView.StructureViewTreeElement[]</span></span><br><span class="line"><span class="comment">     **/</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> StructureViewTreeElement[] getChildren(PsiElement psiElement)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">final</span> PsiClass parentClass = (PsiClass) psiElement;</span><br><span class="line">        <span class="keyword">final</span> Stream&lt;PsiMethodTreeElement&gt; methods = Arrays.stream(parentClass.getMethods())</span><br><span class="line">                .filter(MyLightMethodBuilder.class::isInstance)</span><br><span class="line">                .map(psiMethod -&gt; <span class="keyword">new</span> PsiMethodTreeElement(psiMethod, <span class="keyword">false</span>));</span><br><span class="line">        <span class="keyword">final</span> Stream&lt;PsiFieldTreeElement&gt; fields = Arrays.stream(parentClass.getFields())</span><br><span class="line">                .filter(MyLightFieldBuilder.class::isInstance)</span><br><span class="line">                .map(psiField -&gt; <span class="keyword">new</span> PsiFieldTreeElement(psiField, <span class="keyword">false</span>));</span><br><span class="line">        <span class="keyword">final</span> Stream&lt;JavaClassTreeElement&gt; classes = Arrays.stream(parentClass.getInnerClasses())</span><br><span class="line">                .filter(MyLightClassBuilder.class::isInstance)</span><br><span class="line">                .map(psiClass -&gt; <span class="keyword">new</span> JavaClassTreeElement(psiClass, <span class="keyword">false</span>));</span><br><span class="line">        <span class="keyword">return</span> Stream.concat(Stream.concat(fields, methods), classes).toArray(StructureViewTreeElement[]::<span class="keyword">new</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Nullable</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">getCurrentEditorElement</span><span class="params">(Editor editor, PsiElement psiElement)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="over"><a href="#over" class="headerlink" title="over"></a>over</h4><ul>
<li>项目源码：<a href="https://github.com/Sictiy/JServer/tree/master/IdeaPlugin" target="_blank" rel="noopener">github</a></li>
<li>编译运行后的样子：<br><img src="https://s1.ax1x.com/2020/03/16/8J5CWQ.png" alt="result"></li>
</ul>

      </div>
      
      
        <br>
        


  <section class='meta' id="footer-meta">
    <div class='new-meta-box'>
      
        
          <div class="new-meta-item date" itemprop="dateUpdated" datetime="2020-03-16T17:47:12+08:00">
  <a class='notlink'>
    <i class="fas fa-clock" aria-hidden="true"></i>
    <p>updated at Mar 16, 2020</p>
  </a>
</div>

        
      
        
          
  
  <div class="new-meta-item meta-tags"><a class="tag" href="/hblog/tags/idea/" rel="nofollow"><i class="fas fa-tag" aria-hidden="true"></i><p>idea</p></a></div>


        
      
        
          
  <div class="new-meta-item share -mob-share-list">
  <div class="-mob-share-list share-body">
    
      
        <a class="-mob-share-qq" title="QQ好友" rel="external nofollow noopener noreferrer"
          
          href="http://connect.qq.com/widget/shareqq/index.html?url=https://www.sictiy.cn/hblog/2020/03/16/idea%E6%8F%92%E4%BB%B6/&title=idea插件开发（实现@SingleInstance注解增加getInstance方法） | SictiyLeon&summary=之前实现了@SingleInstance注解的编译器处理器，实现添加@SingleInstance就可以把类变成单例，这里记录一下实现让idea也能看懂这个注解的的插件的过程。"
          
          >
          
            <img src="https://cdn.jsdelivr.net/gh/xaoxuu/assets@19.1.9/logo/128/qq.png">
          
        </a>
      
    
      
        <a class="-mob-share-qzone" title="QQ空间" rel="external nofollow noopener noreferrer"
          
          href="https://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?url=https://www.sictiy.cn/hblog/2020/03/16/idea%E6%8F%92%E4%BB%B6/&title=idea插件开发（实现@SingleInstance注解增加getInstance方法） | SictiyLeon&summary=之前实现了@SingleInstance注解的编译器处理器，实现添加@SingleInstance就可以把类变成单例，这里记录一下实现让idea也能看懂这个注解的的插件的过程。"
          
          >
          
            <img src="https://cdn.jsdelivr.net/gh/xaoxuu/assets@19.1.9/logo/128/qzone.png">
          
        </a>
      
    
      
        <a class="-mob-share-weibo" title="微博" rel="external nofollow noopener noreferrer"
          
          href="http://service.weibo.com/share/share.php?url=https://www.sictiy.cn/hblog/2020/03/16/idea%E6%8F%92%E4%BB%B6/&title=idea插件开发（实现@SingleInstance注解增加getInstance方法） | SictiyLeon&summary=之前实现了@SingleInstance注解的编译器处理器，实现添加@SingleInstance就可以把类变成单例，这里记录一下实现让idea也能看懂这个注解的的插件的过程。"
          
          >
          
            <img src="https://cdn.jsdelivr.net/gh/xaoxuu/assets@19.1.9/logo/128/weibo.png">
          
        </a>
      
    
  </div>
</div>



        
      
    </div>
  </section>


      
      
          <div class="prev-next">
              
              
                  <section class="next">
                      <span class="art-item-right" aria-hidden="true">
                          <h6>Next&nbsp;<i class="fas fa-chevron-right" aria-hidden="true"></i></h6>
                          <h4>
                              <a href="/hblog/2020/03/09/singleInstance/" rel="prev" title="lombok简单使用，简单实现单例注解及修改lombokidea插件支持新增的单例注解">
                                  
                                      lombok简单使用，简单实现单例注解及修改lombokidea插件支持新增的单例注解
                                  
                              </a>
                          </h4>
                          
                              
                              <h6 class="tags">
                                  <a class="tag" href="/hblog/tags/idea/"><i class="fas fa-tag fa-fw" aria-hidden="true"></i> idea</a>
                              </h6>
                          
                      </span>
                  </section>
              
          </div>
      
    </section>
  </article>



  <!-- 显示推荐文章和评论 -->



  <article class="post white-box comments">
    <section class="article typo">
      <h4><i class="fas fa-comments fa-fw" aria-hidden="true"></i>&nbsp;Comments</h4>
      
      
      
      
        <section id="comments">
          <div id="valine_container" class="valine_thread">
            <i class="fas fa-spinner fa-spin fa-fw"></i>
          </div>
        </section>
      
    </section>
  </article>






<!-- 根据页面mathjax变量决定是否加载MathJax数学公式js -->



  <script>
    window.subData = {
      title: 'idea插件开发（实现@SingleInstance注解增加getInstance方法）',
      tools: true
    }
  </script>


</div>
<aside class='l_side'>
  
    
    
      
      
        
          
          
            
              <section class='widget author'>
  <div class='content material'>
    
      <div class='avatar'>
        <img class='avatar' src='https://s2.ax1x.com/2020/01/17/lxqAEV.jpg'/>
      </div>
    
    
    
      <div class="social-wrapper">
        
          
            <a href="/hblog/xlm104600@gmail.com"
              class="social fas fa-envelope flat-btn"
              target="_blank"
              rel="external nofollow noopener noreferrer">
            </a>
          
        
          
            <a href="https://github.com/Sictiy"
              class="social fab fa-github flat-btn"
              target="_blank"
              rel="external nofollow noopener noreferrer">
            </a>
          
        
          
            <a href="https://music.163.com/#/user/home?id=98830575"
              class="social fas fa-headphones-alt flat-btn"
              target="_blank"
              rel="external nofollow noopener noreferrer">
            </a>
          
        
      </div>
    
  </div>
</section>

            
          
        
          
          
        
          
          
        
          
          
        
          
          
        
          
          
        
          
          
        
      
        
          
          
        
          
          
        
          
          
            
              <section class='widget grid'>
  
<header class='material'>
  <div><i class="fas fa-map-signs fa-fw" aria-hidden="true"></i>&nbsp;&nbsp;站内导航</div>
  
</header>

  <div class='content material'>
    <ul class="grid navgation">
      
        <li><a class="flat-box" title="/hblog/" href="/hblog/"
          
          
          id="hblog">
          
            <i class="fas fa-clock fa-fw" aria-hidden="true"></i>
          
          近期文章
        </a></li>
      
        <li><a class="flat-box" title="/hblog/archives/" href="/hblog/archives/"
          
            rel="nofollow"
          
          
          id="hblogarchives">
          
            <i class="fas fa-archive fa-fw" aria-hidden="true"></i>
          
          文章归档
        </a></li>
      
        <li><a class="flat-box" title="/hblog/about/" href="/hblog/about/"
          
            rel="nofollow"
          
          
          id="hblogabout">
          
            <i class="fas fa-info-circle fa-fw" aria-hidden="true"></i>
          
          关于小站
        </a></li>
      
    </ul>
  </div>
</section>

            
          
        
          
          
        
          
          
        
          
          
        
          
          
        
      
        
          
          
        
          
          
            
              
  <section class='widget toc-wrapper'>
    
<header class='material'>
  <div><i class="fas fa-list fa-fw" aria-hidden="true"></i>&nbsp;&nbsp;本文目录</div>
  
    <!-- <div class='wrapper'><a class="s-toc rightBtn" rel="external nofollow noopener noreferrer" href="javascript:void(0)"><i class="fas fa-thumbtack fa-fw"></i></a></div> -->
  
</header>

    <div class='content material'>
      <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#Lombok-Idea-插件修改"><span class="toc-text">Lombok Idea 插件修改</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#跑一下原Lombok-Idea-插件"><span class="toc-text">跑一下原Lombok Idea 插件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#阅读Lombok-Idea-插件源码"><span class="toc-text">阅读Lombok Idea 插件源码</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#新增-SingleInstance的处理"><span class="toc-text">新增@SingleInstance的处理</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#简化体验idea插件开发"><span class="toc-text">简化体验idea插件开发</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#plugin-xml"><span class="toc-text">plugin.xml</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#主要功能扩展接口"><span class="toc-text">主要功能扩展接口</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#开始动手"><span class="toc-text">开始动手</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#Action"><span class="toc-text">Action</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#Processor"><span class="toc-text">Processor</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#Extends"><span class="toc-text">Extends</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#PsiAugmentProvider"><span class="toc-text">PsiAugmentProvider</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#StructureViewExtension"><span class="toc-text">StructureViewExtension</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#over"><span class="toc-text">over</span></a></li></ol></li></ol></li></ol>
    </div>
  </section>


            
          
        
          
          
        
          
          
        
          
          
        
          
          
        
          
          
        
      
        
          
          
        
          
          
        
          
          
        
          
          
            
              
  <section class='widget category'>
    
<header class='material'>
  <div><i class="fas fa-folder-open fa-fw" aria-hidden="true"></i>&nbsp;&nbsp;文章分类</div>
  
    <a class="rightBtn"
    
      rel="nofollow"
    
    
    href="/hblog/categories/"
    title="categories/">
    <i class="fas fa-expand-arrows-alt fa-fw"></i></a>
  
</header>

    <div class='content material'>
      <ul class="entry">
        
          <li><a class="flat-box" title="/hblog/categories/algorithm/" href="/hblog/categories/algorithm/"><div class='name'>algorithm</div><div class='badge'>(2)</div></a></li>
        
          <li><a class="flat-box" title="/hblog/categories/c/" href="/hblog/categories/c/"><div class='name'>c++</div><div class='badge'>(1)</div></a></li>
        
          <li><a class="flat-box" title="/hblog/categories/go/" href="/hblog/categories/go/"><div class='name'>go</div><div class='badge'>(1)</div></a></li>
        
          <li><a class="flat-box" title="/hblog/categories/java/" href="/hblog/categories/java/"><div class='name'>java</div><div class='badge'>(5)</div></a></li>
        
          <li><a class="flat-box" title="/hblog/categories/server/" href="/hblog/categories/server/"><div class='name'>server</div><div class='badge'>(2)</div></a></li>
        
          <li><a class="flat-box" title="/hblog/categories/%E6%B5%8B%E8%AF%95/" href="/hblog/categories/%E6%B5%8B%E8%AF%95/"><div class='name'>测试</div><div class='badge'>(1)</div></a></li>
        
          <li><a class="flat-box" title="/hblog/categories/%E9%85%8D%E7%BD%AE/" href="/hblog/categories/%E9%85%8D%E7%BD%AE/"><div class='name'>配置</div><div class='badge'>(1)</div></a></li>
        
      </ul>
    </div>
  </section>


            
          
        
          
          
        
          
          
        
          
          
        
      
    

  
</aside>

<footer id="footer" class="clearfix">
  
  
    <div class="social-wrapper">
      
        
          <a href="/hblog/xlm104600@gmail.com"
            class="social fas fa-envelope flat-btn"
            target="_blank"
            rel="external nofollow noopener noreferrer">
          </a>
        
      
        
          <a href="https://github.com/Sictiy"
            class="social fab fa-github flat-btn"
            target="_blank"
            rel="external nofollow noopener noreferrer">
          </a>
        
      
        
          <a href="https://music.163.com/#/user/home?id=98830575"
            class="social fas fa-headphones-alt flat-btn"
            target="_blank"
            rel="external nofollow noopener noreferrer">
          </a>
        
      
    </div>
  
  <br>
  <div><p>Blog content follows the <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en" target="_blank" rel="noopener">Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) License</a></p>
</div>
  <div>
    Use
    <a href="https://xaoxuu.com/wiki/material-x/" target="_blank" class="codename">Material X</a>
    as theme
    
      , 
      total visits
      <span id="busuanzi_value_site_pv"><i class="fas fa-spinner fa-spin fa-fw" aria-hidden="true"></i></span>
      times
    
    . 
  </div>
</footer>
<script>setLoadingBarProgress(80);</script>


      <script>setLoadingBarProgress(60);</script>
    </div>
    <a class="s-top fas fa-arrow-up fa-fw" href='javascript:void(0)'></a>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/jquery@3.3.1/dist/jquery.min.js"></script>

  <script>
    var GOOGLE_CUSTOM_SEARCH_API_KEY = "";
    var GOOGLE_CUSTOM_SEARCH_ENGINE_ID = "";
    var ALGOLIA_API_KEY = "";
    var ALGOLIA_APP_ID = "";
    var ALGOLIA_INDEX_NAME = "";
    var AZURE_SERVICE_NAME = "";
    var AZURE_INDEX_NAME = "";
    var AZURE_QUERY_KEY = "";
    var BAIDU_API_ID = "";
    var SEARCH_SERVICE = "hexo" || "hexo";
    var ROOT = "/hblog/"||"/";
    if(!ROOT.endsWith('/'))ROOT += '/';
  </script>

<script src="//instant.page/1.2.2" type="module" integrity="sha384-2xV8M5griQmzyiY3CDqh1dn4z3llDVqZDqzjzcY+jCBCk/a5fXJmuZ/40JJAPeoU"></script>


  <script async src="https://cdn.jsdelivr.net/npm/scrollreveal@4.0.5/dist/scrollreveal.min.js"></script>
  <script type="text/javascript">
    $(function() {
      const $reveal = $('.reveal');
      if ($reveal.length === 0) return;
      const sr = ScrollReveal({ distance: 0 });
      sr.reveal('.reveal');
    });
  </script>


  <script src="https://cdn.jsdelivr.net/npm/node-waves@0.7.6/dist/waves.min.js"></script>
  <script type="text/javascript">
    $(function() {
      Waves.attach('.flat-btn', ['waves-button']);
      Waves.attach('.float-btn', ['waves-button', 'waves-float']);
      Waves.attach('.float-btn-light', ['waves-button', 'waves-float', 'waves-light']);
      Waves.attach('.flat-box', ['waves-block']);
      Waves.attach('.float-box', ['waves-block', 'waves-float']);
      Waves.attach('.waves-image');
      Waves.init();
    });
  </script>


  <script async src="https://cdn.jsdelivr.net/gh/xaoxuu/cdn-busuanzi@2.3/js/busuanzi.pure.mini.js"></script>


  <!-- fastclick -->
  <script src="https://cdn.jsdelivr.net/npm/fastclick@1.0.6/lib/fastclick.min.js"></script>
  <script>
    document.addEventListener('DOMContentLoaded', function() {
      FastClick.attach(document.body)
    }, false)
  </script>



  
  
  
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-backstretch/2.0.4/jquery.backstretch.min.js"></script>
    <script type="text/javascript">
      $(function(){
        if ('') {
          $('').backstretch(
          ["https://img.vim-cn.com/29/91197b04c13f512f734a76d4ac422d89dbe229.jpg"],
          {
            duration: "6000",
            fade: "2500"
          });
        } else {
          $.backstretch(
          ["https://img.vim-cn.com/29/91197b04c13f512f734a76d4ac422d89dbe229.jpg"],
          {
            duration: "6000",
            fade: "2500"
          });
        }
      });
    </script>
  









  <script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>
  
    
      
<script src="https://cdn.jsdelivr.net/gh/xaoxuu/volantis@1.0.6/js/volantis.min.js"></script>

    
  
  <script>
  var GUEST_INFO = ['nick','mail','link'];
  var guest_info = 'nick,mail,link'.split(',').filter(function(item){
    return GUEST_INFO.indexOf(item) > -1
  });
  var notify = 'true' == true;
  var verify = 'true' == true;
  var valine = new Valine();
  valine.init({
    el: '#valine_container',
    notify: notify,
    verify: verify,
    guest_info: guest_info,
    
    appId: "DOrDaqtmOYARAQcfWKvJLWHi-MdYXbMMI",
    appKey: "8987UCDFR7NQIATt7ja3iojv",
    placeholder: "快来评论吧~",
    pageSize:'10',
    avatar:'mp',
    lang:'zh-cn',
    highlight:'true'
  })
  </script>



  
<script src="https://cdn.jsdelivr.net/gh/xaoxuu/cdn-material-x@19.11/js/app.js"></script>



  
<script src="https://cdn.jsdelivr.net/gh/xaoxuu/cdn-material-x@19.11/js/search.js"></script>





<!-- 复制 -->
<script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
<script>
  let COPY_SUCCESS = "Copied";
  let COPY_FAILURE = "Copy failed";
  /*页面载入完成后，创建复制按钮*/
  !function (e, t, a) {
    /* code */
    var initCopyCode = function(){
      var copyHtml = '';
      copyHtml += '<button class="btn-copy" data-clipboard-snippet="">';
      copyHtml += '  <i class="fa fa-copy"></i><span>Copy</span>';
      copyHtml += '</button>';
      $(".highlight .code pre").before(copyHtml);
      var clipboard = new ClipboardJS('.btn-copy', {
        target: function(trigger) {
          return trigger.nextElementSibling;
        }
      });

      clipboard.on('success', function(e) {
        //您可以加入成功提示
        console.info('Action:', e.action);
        console.info('Text:', e.text);
        console.info('Trigger:', e.trigger);
        success_prompt(COPY_SUCCESS);
        e.clearSelection();
      });
      clipboard.on('error', function(e) {
        //您可以加入失败提示
        console.error('Action:', e.action);
        console.error('Trigger:', e.trigger);
        fail_prompt(COPY_FAILURE);
      });
    }
    initCopyCode();

  }(window, document);

  /**
   * 弹出式提示框，默认1.5秒自动消失
   * @param message 提示信息
   * @param style 提示样式，有alert-success、alert-danger、alert-warning、alert-info
   * @param time 消失时间
   */
  var prompt = function (message, style, time)
  {
      style = (style === undefined) ? 'alert-success' : style;
      time = (time === undefined) ? 1500 : time*1000;
      $('<div>')
          .appendTo('body')
          .addClass('alert ' + style)
          .html(message)
          .show()
          .delay(time)
          .fadeOut();
  };

  // 成功提示
  var success_prompt = function(message, time)
  {
      prompt(message, 'alert-success', time);
  };

  // 失败提示
  var fail_prompt = function(message, time)
  {
      prompt(message, 'alert-danger', time);
  };

  // 提醒
  var warning_prompt = function(message, time)
  {
      prompt(message, 'alert-warning', time);
  };

  // 信息提示
  var info_prompt = function(message, time)
  {
      prompt(message, 'alert-info', time);
  };

</script>


<!-- fancybox -->
<script src="https://cdn.jsdelivr.net/gh/fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.js"></script>
<script>
  let LAZY_LOAD_IMAGE = "";
  $(".article-entry").find("fancybox").find("img").each(function () {
      var element = document.createElement("a");
      $(element).attr("data-fancybox", "gallery");
      $(element).attr("href", $(this).attr("src"));
      /* 图片采用懒加载处理时,
       * 一般图片标签内会有个属性名来存放图片的真实地址，比如 data-original,
       * 那么此处将原本的属性名src替换为对应属性名data-original,
       * 修改如下
       */
       if (LAZY_LOAD_IMAGE) {
         $(element).attr("href", $(this).attr("data-original"));
       }
      $(this).wrap(element);
  });
</script>





  <script>setLoadingBarProgress(100);</script>
</body>
</html>
